/*
AcessOS 0.1
System Calls
*/
#include <acess.h>
#include <syscalls.h>

#define	SYSCALLS_DEBUG	0
#define DEBUG	(SYSCALLS_DEBUG | ACESS_DEBUG)

//IMPOTRTS
extern void syscall_stub();
extern void	MM_Dump();
extern int	Proc_SendMessage(Uint Dest, Uint Data[4], int sync);
extern int	Proc_GetMessage(Uint *Source, Uint *Data[4]);
extern int	Proc_GetTid();
extern int	Proc_GetPid();
extern int	Proc_Clone(void *UserStack);
extern int	MM_IsUser(Uint addr);

//PROTOTYPES
void syscall_install();
Uint syscall_handler(struct regs *r);
//Uint syscall_linuxhandler(struct regs *r);

int syscall_version	(Uint *err, Uint buffer, Uint flags);
int syscall_exit	(Uint *err);
int syscall_open	(Uint *err, Uint pathAddr, int flags, int mode);
int syscall_close	(Uint *err, int fp);
int syscall_read	(Uint *err, int fp, unsigned int length, Uint bufferAddr);
int syscall_write	(Uint *err, int fp, unsigned int length,  Uint strAddr);
int syscall_seek	(Uint *err, int fp, unsigned int dist, int dir);
int syscall_tell	(Uint *err, int fp);
int syscall_fstat	(Uint *err, int fp, Uint buf);
int syscall_readdir	(Uint *err, int fp, Uint nameAddr);
int syscall_ioctl	(Uint *err, int fp, int id, Uint dataAddr);
int syscall_kexec	(Uint *err, Uint32 cmd, Uint args, Uint envp);
int syscall_fork	(Uint *err);
int syscall_execve	(Uint *err, Uint32 cmd, Uint args, Uint envp);
int syscall_waitpid	(Uint *err, Uint pid, Uint action);
int syscall_brk		(Uint *err, Uint pos);
int syscall_ldbin	(Uint *err, Uint path, Uint entry);
int syscall_debug	(Uint *err, Uint fmt, Uint arg1, Uint arg2, Uint arg3, Uint arg4);
int syscall_yield	(Uint *err);
int syscall_sendmsg	(Uint *err, Uint dest, Uint dataptr);
int syscall_sendmsgandwait(Uint *err, Uint dest, Uint dataptr);
int syscall_getmsg	(Uint *err, Uint destptr, Uint dataptr);
int syscall_pollmsg	(Uint *err, Uint destptr, Uint dataptr);
int syscall_clone	(Uint *Err, Uint NewStack);
int syscall_texit	(Uint *Err, Uint Ret);

//CODE
void Syscall_Install()
{
	IDT_SetGate(0xAC, (Uint32)syscall_stub, 0x08, 0xEF);	//32bit Trap Gate (Instead of Interrupt) - User Callable
	//idt_set_gate(0x80, (Uint32)syscall_linuxstub, 0x08, 0x8E);
}

Uint syscall_handler(struct regs *r)
{
	 int	ret;
	Uint	errno = 0;
	#if DEBUG
		LogF("syscall_handler: #%i\n", r->eax);
		LogF(" syscall_handler: EBX=%x,ECX=%x,EDX=%x\n", r->ebx, r->ecx, r->edx);
	#endif
	
	switch(r->eax)
	{
	case SYS_NULL:		ret = syscall_version	(&errno, r->ebx, r->ecx);	break;
	case SYS_EXIT:		ret = syscall_exit	(&errno);			break;
	case SYS_OPEN:		ret = syscall_open	(&errno, r->ebx, r->ecx, r->edx);	break;
	case SYS_CLOSE:		ret = syscall_close(&errno, r->ebx);	break;
	case SYS_READ:		ret = syscall_read	(&errno, r->ebx, r->ecx, r->edx);	break;
	case SYS_WRITE:		ret = syscall_write(&errno, r->ebx, r->ecx, r->edx);	break;
	case SYS_SEEK:		ret = syscall_seek	(&errno, r->ebx, r->ecx, r->edx);	break;
	case SYS_TELL:		ret = syscall_tell	(&errno, r->ebx);	break;
	case SYS_FSTAT:		ret = syscall_fstat(&errno, r->ebx, r->ecx);	break;
		
	case SYS_READDIR:	ret = syscall_readdir	(&errno, r->ebx, r->ecx);		break;
	case SYS_IOCTL:		ret = syscall_ioctl	(&errno, r->ebx, r->ecx, r->edx);	break;
		
	case SYS_KEXEC:		ret = syscall_kexec	(&errno, r->ebx, r->ecx, r->edx);	break;
	case SYS_EXECVE:	ret = syscall_execve	(&errno, r->ebx, r->ecx, r->edx);	break;
	case SYS_FORK:		ret = syscall_fork		(&errno);	break;
	case SYS_WAITPID:	ret = syscall_waitpid	(&errno, r->ebx, r->ecx);	break;
	case SYS_YIELD:		ret = syscall_yield	(&errno);		break;
	
	case SYS_SENDMSG:	ret = syscall_sendmsg	(&errno, r->ebx, r->ecx);	break;
	
	case SYS_SENDMSGANDWAIT:
						ret = syscall_sendmsgandwait(&errno, r->ebx, r->ecx);	break;
	
	case SYS_GETMSG:	ret = syscall_getmsg	(&errno, r->ebx, r->ecx);	break;
	case SYS_POLLMSG:	ret = syscall_pollmsg	(&errno, r->ebx, r->ecx);	break;
	
	case SYS_GETTID:	ret = Proc_GetTid();	break;
	case SYS_GETPID:	ret = Proc_GetPid();	break;
	
	case SYS_BRK:		ret = syscall_brk	(&errno, r->ebx);	break;
	case SYS_LDBIN:		ret = syscall_ldbin(&errno, r->ebx, r->ecx);	break;
	
	case SYS_DEBUG:		ret = syscall_debug(&errno, r->ebx, r->ecx, r->edx, r->edi, r->esi);	break;
	
	case SYS_CLONE:		ret = syscall_clone(&errno, r->ebx);	break;
	case SYS_TEXIT:		ret = syscall_texit(&errno, r->ebx);	break;
	
	case SYS_ULDBIN:
		WarningEx("USER", "Syscall 'Binary Unload' is unimplemented");
		r->ebx = ERR_NOSYS;
		return 0;
	
	default:
		WarningEx("USER", "Bad Syscall #%i, Pid %i", r->eax, proc_pid);
		r->ebx = ERR_NOSYS;
		return 0;
	}
	
	r->ebx = errno;
	return ret;
}

/**
 * \fn int Syscall_CheckMem(Uint *err, Uint Addr, Uint Size)
 * \brief Check if the address and size are \b user accessable
 * \param err	Pointer to error variable
 * \param Addr	Address to check
 * \param Size	Size of block
 */
int Syscall_CheckMem(Uint *err, Uint Addr, Uint Size)
{
	Uint	tmpAddr;
	if(Addr == 0)	return 1;	// NULL is ALWAYS handled later on
	
	for(tmpAddr = ((Addr & 0xFFF) + Size) >> 12;
		tmpAddr >= (Addr & ~0xFFF);
		tmpAddr -= 0x1000)
	{
		if( !MM_IsValid(tmpAddr) || !MM_IsUser(tmpAddr) ) {
			*err = ERR_BADMEM;
			return 0;
		}
	}
	return 1;
}

/**
 \fn int syscall_version (Uint *err, Uint buffer, Uint flags)
 \brief Returns the Acess Version String
 \param err	Pointer to EBX for Error Code
 \param buffer	Destination buffer for version string
 \param flags	String flags - unused ATM
 \todo	Implement Completely
*/
#define	ACESS_VER_STR	"AcessOS v1"
int syscall_version(Uint *err, Uint buffer, Uint flags)
{
	if(buffer == 0)	return sizeof(ACESS_VER_STR);
	if( !Syscall_CheckMem(err, buffer, sizeof(ACESS_VER_STR)+1) )	return -1;
	strcpy((char*)buffer, ACESS_VER_STR);
	return 0;
}

/**
 \fn int syscall_exit(Uint *err)
 \brief Quits the current process
 \param err		Pointer to EBX
*/
int	syscall_exit(Uint *err)
{
	#if DEBUG
		LogF("syscall_exit: ()\n");
	#endif
	Proc_Exit();	// Exit Process
	for(;;)	Proc_Yield();	// Inifinitely Yield until process is destroyed
	return 1;
}

/**
 \fn int syscall_open(Uint *err, Uint pathAddr, int flags, int mode)
 \brief Opens a file with the VFS
 \param err		Pointer to EBX (errno)
 \param pathAddr	Address of the desired path
 \param flags	Open Flags
 \param	mode	Open Mode (Is this needed?)
*/
int syscall_open(Uint *err, Uint pathAddr, int flags, int mode)
{
	#if DEBUG
		LogF("syscall_open: (pathAddr=0x%x('%s'),flags=%i,mode=%i)\n", pathAddr,(char*)pathAddr,flags,mode);
	#endif
	if( !Syscall_CheckMem(err, pathAddr, 0) )	return -1;	// Unreliable as size is unknown
	return vfs_open((char *)pathAddr, flags|VFS_OPENFLAG_USER);
}

/**
 * \fn int syscall_close(Uint *err, int fp)
 * \brief Close the passed file pointer
 * \param err	Pointer to EBX for error code
*/
int syscall_close(Uint *err, int fp)
{
	vfs_close(fp);
	return 1;
}

/**
 * \fn int syscall_read(Uint *err, int fp, unsigned int length, Uint bufferAddr)
 * \brief Read from a file to a buffer
 * \param err	Pointer to EBX for error code
 */
int syscall_read(Uint *err, int fp, unsigned int length, Uint bufferAddr)
{
	#if DEBUG >= 2
		LogF("syscall_read: (fp=%i,length=%i,strAddr=0x%x)\n", fp, length, bufferAddr);
	#endif
	if( !Syscall_CheckMem(err, bufferAddr, length) )	return -1;
	return vfs_read(fp, length, (char *)bufferAddr);
}

/**
 * \fn int syscall_write(Uint *err, int fp, unsigned int length, Uint bufferAddr)
 * \brief Write to a file from a buffer
 * \param err	Pointer to EBX for error code
 */
int syscall_write(Uint *err, int fp, unsigned int length, Uint bufferAddr)
{
	#if DEBUG >= 2
		LogF("syscall_write: (fp=%i,length=%i,strAddr=0x%x)\n", fp, length, bufferAddr);
	#endif
	if( !Syscall_CheckMem(err, bufferAddr, length) )	return -1;
	return vfs_write(fp, length, (char *)bufferAddr);
}

/**
 * \fn int syscall_seek(Uint *err, int fp, unsigned int dist, int dir)
 * \brief Alter the file position
 * \param err	Pointer to EBX for error code
 */
int syscall_seek(Uint *err, int fp, unsigned int dist, int dir)
{
	#if DEBUG >= 2
		LogF("syscall_seek: (fp=%i,dist=%i,dir=0x%x)\n", fp, dist, dir);
	#endif
	vfs_seek(fp, dist, dir);
	return 1;
}

/**
 * \fn int syscall_tell(Uint *err, int fp)
 * \brief Get the current file position
 * \param err	Pointer to EBX for error code
 */
int syscall_tell(Uint *err, int fp)
{
	#if DEBUG
		LogF("syscall_tell: (fp=%i)\n", fp);
	#endif
	return vfs_tell(fp);
}

/**
 * \fn int syscall_readdir(Uint *err, int fp, Uint nameAddr)
 * \brief Read from a directory
 * \param err	Pointer to EBX for error code
 */
int syscall_readdir(Uint *err, int fp, Uint nameAddr)
{
	#if DEBUG
		LogF("syscall_readdir: (fp=%i, nameAddr=0x%x)\n", fp, nameAddr);
	#endif
	if( !Syscall_CheckMem(err, nameAddr, 0) )	return 0;
	return vfs_readdir(fp, (char *)nameAddr);
}

/**
 * \fn int syscall_fstat(Uint *err, int fp, Uint buf)
 * \brief Get information of a file
 * \param err	Pointer to EBX for error code
 */
int syscall_fstat(Uint *err, int fp, Uint buf)
{
	#if DEBUG
		LogF("syscall_fstat: (fp=%i, buf=0x%x)\n", fp, buf);
	#endif
	if( !Syscall_CheckMem(err, buf, sizeof(t_fstat)) )	return -1;
	if(vfs_stat(fp, (t_fstat*)buf) == 0) {
		return -1;
	}
	return 1;
}

/**
 * \fn int syscall_ioctl(Uint *err, int fp, int id, Uint dataAddr)
 * \brief Call an IO Controll on a file pointer
 * \param err	Pointer to EBX for error code
 */
int syscall_ioctl(Uint *err, int fp, int id, Uint dataAddr)
{
	if( !Syscall_CheckMem(err, dataAddr, 4) )	return -1;
	return vfs_ioctl(fp, id, (void*)dataAddr);
}

/**
 * \fn int syscall_execve(Uint *err, Uint32 cmd, Uint argp, Uint envp)
 * \param Replace the current process image with another
 * \param err	Pointer to EBX for error code
 * \return -1 on error, or no return
 */
int syscall_execve(Uint *err, Uint32 cmd, Uint argp, Uint envp)
{
	if( !Syscall_CheckMem(err, cmd, 0) )	return -1;
	if( !Syscall_CheckMem(err, argp, 0) )	return -1;
	if( !Syscall_CheckMem(err, envp, 0) )	return -1;
	return Proc_Execve((char*)cmd, (char**)argp, (char**)envp, 3);
}

/**
 * \fn int syscall_fork(Uint *err)
 * \brief Fork the current process
 * \param err	Pointer to EBX for error code
 */
int syscall_fork(Uint *err)
{
	#if DEBUG
	int ret = Proc_Fork();
	__asm__ ("cli");
	MM_Dump();
	__asm__ ("sti");
	return ret;
	#else
	return Proc_Fork();
	#endif
}

/**
 * \fn int syscall_kexec(Uint *err, Uint32 cmd, Uint argp, Uint envp)
 * \brief Create a new process from the image provided
 * \param err	Pointer to EBX for error code
 * \param cmd	Path to image
 * \param argp	Arguments
 * \param envp	Environment
 */
int syscall_kexec(Uint *err, Uint32 cmd, Uint argp, Uint envp)
{
	if( !Syscall_CheckMem(err, cmd, 0) )	return -1;
	if( !Syscall_CheckMem(err, argp, 0) )	return -1;
	if( !Syscall_CheckMem(err, envp, 0) )	return -1;
	return Proc_Open((char*)cmd, (char**)argp, (char**)envp);
}

/**
 * \fn int syscall_waitpid(Uint *err, Uint pid, Uint action)
 * \brief Wait for a process to do a specific action
 * \param err	Pointer to EBX for error code
 * \param pid	Process to wait for
 * \param action	Action to wait for
 */
int syscall_waitpid(Uint *err, Uint pid, Uint action)
{
	return Proc_WaitPid(pid, action);
}

/**
 * \fn int syscall_brk(Uint *err, Uint pos)
 * \brief Alter the user's memory space
 * \param err	Pointer to EBX for error code
 * \param pos	Destination position
 */
int syscall_brk(Uint *err, Uint pos)
{
	#if DEBUG
	Uint ret;
	ret = proc_brk(pos);
	LogF("syscall_brk: RETURN 0x%x\n", ret);
	return ret;
	#else
	return proc_brk(pos);
	#endif
}

/**
 * \fn int syscall_yield(Uint *err)
 * \brief Yield current timeslice
 * \param err	Pointer to EBX for error code
 */
int syscall_yield(Uint *err)
{
	Proc_Yield();
	return 0;
}

/**
 * \fn int syscall_sendmsg(Uint *err, Uint dest, Uint dataptr)
 * \brief Send an IPC Message asynchronously
 * \param err	Pointer to EBX for error
 * \param dest	Destination PID
 * \param dataptr	Pointer to message data
 */
int syscall_sendmsg(Uint *err, Uint dest, Uint dataptr)
{
	if(!Syscall_CheckMem(err, dataptr, 16))	return 0;
	return Proc_SendMessage(dest, (void*)dataptr, 0);
}

/**
 * \fn int syscall_sendmsg(Uint *err, Uint dest, Uint dataptr)
 * \brief Send an IPC Message asynchronously
 * \param err	Pointer to EBX for error
 */
int syscall_sendmsgandwait(Uint *err, Uint dest, Uint dataptr)
{
	if(!MM_IsValid(dataptr))	return 0;
	return Proc_SendMessage(dest, (void*)dataptr, 1);
}

/**
 * \fn int syscall_getmsg(Uint *err, Uint destptr, Uint dataptr)
 * \brief Check for a message
 */
int syscall_getmsg(Uint *err, Uint destptr, Uint dataptr)
{
	if( !Syscall_CheckMem(err, destptr, 4) )	return -1;
	if( !Syscall_CheckMem(err, dataptr, 16) )	return -1;
	return Proc_GetMessage((Uint*)destptr, (void*)dataptr);
}
/**
 * \fn int syscall_pollmsg(Uint *err, Uint destptr, Uint dataptr)
 * \brief Poll for a message
 * \param err	Pointer to EBX for error code
 */
int syscall_pollmsg(Uint *err, Uint destptr, Uint dataptr)
{
	if( !Syscall_CheckMem(err, destptr, 4) )	return -1;
	if( !Syscall_CheckMem(err, dataptr, 16) )	return -1;
	while( Proc_GetMessage((Uint*)destptr, (void*)dataptr) == 0 )
		Proc_Yield();
	return 1;
}

/**
 \fn int syscall_ldbin(Uint *err, Uint path, Uint entry)
 \brief Loads a binary file into the current process space and returns it's base
 \param	err		Pointer to EBX (errno)
 \param	path	Binary Path (String Pointer)
 \param	entry	Pointer to entrypoint
 \return	Base address of binary
*/
int syscall_ldbin(Uint *err, Uint path, Uint entry)
{
	if( !Syscall_CheckMem(err, path, 0) )	return -1;
	if( !Syscall_CheckMem(err, entry, 4) )	return -1;
	return Binary_Load((char*)path, (Uint*)entry);
}

/**
 \fn int syscall_debug(Uint *err, Uint fmt, Uint arg1, Uint arg2, Uint arg3, Uint arg4)
 \brief Allows a user application to easily print debug text
 \note Only avaliable in non-production builds
*/
int syscall_debug(Uint *err, Uint fmt, Uint arg1, Uint arg2, Uint arg3, Uint arg4)
{
	#if !PRODUCTION
	#if DEBUG
	LogF("syscall_debug: (fmt=0x%x, arg1=0x%x, arg2=0x%x, arg3=0x%x, arg4=0x%x)\n",
		fmt, arg1, arg2, arg3, arg4);
	#endif
	LogF("[UDBG] %i: ", proc_pid);
	LogF((char*)fmt, arg1, arg2, arg3, arg4);
	#endif
	return 0;
}

/**
 * \fn int syscall_clone(Uint *Err, Uint NewStack)
 * \brief Create a new thread
 */
int syscall_clone(Uint *Err, Uint NewStack)
{
	if( !Syscall_CheckMem(Err, NewStack, 0) )	return -1;
	return Proc_Clone((void*)NewStack);
}

/**
 * \fn int syscall_texit(Uint *Err, Uint Ret)
 * \note \a Ret is ignored, for now
 */
int syscall_texit(Uint *Err, Uint Ret)
{
	Proc_Exit();
	for(;;) Proc_Yield();
}
